import os
import sys

from PyQt5.QtCore import *
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import *
from pydub import AudioSegment, silence

from ui.mainForm import Ui_MainForm

project_path = os.getcwd()


class WorkThread(QtCore.QObject):
    # 自定义信号对象,参数str就代表这个信号可以传一个字符串
    signal = pyqtSignal([str], [float])

    def __init__(self):
        # 初始化函数
        super(WorkThread, self).__init__()
        self.paths = []
        self.min_length = 0
        self.min_silence_len = 0
        self.min_to_cut = 0

    def reserve(self, paths, min_length, min_silence_len, min_to_cut):
        self.paths = paths
        self.min_length = min_length
        self.min_silence_len = min_silence_len
        self.min_to_cut = min_to_cut

    def run(self):
        wav_output = project_path + "/wav/output/"
        if not os.path.exists(wav_output):
            os.makedirs(wav_output)

        freq = 0
        cnt = len(self.paths)

        for wav_path in self.paths:
            wav_name = wav_path.split("/")[-1:][0].split(".")[0]  # 从路径中截取出不带后缀的文件名
            sound = AudioSegment.from_wav(wav_path)

            if sound.duration_seconds * 1000 < self.min_to_cut:
                sound.export(wav_output + wav_name + ".wav", format="wav")
                self.log(f"原音频短于最小长度{wav_path}")
                continue

            sound_cut_list = silence.split_on_silence(sound, min_silence_len=self.min_silence_len, silence_thresh=-50, keep_silence=200)
            self.log(f"读取文件{wav_path} 截取到[{len(sound_cut_list)}]个！")

            cut_file = AudioSegment.silent(1)  # 占位
            cut_file_backup = AudioSegment.silent(1)  # 保存最后被写入文件的声音文本，以防出现在最后切出一段很短的音频，如果出现这种情况，将其合入前一个音频文件
            i = 0
            for sound_cut in sound_cut_list:
                if cut_file.duration_seconds <= 0.001:
                    cut_file = sound_cut
                else:
                    cut_file = cut_file + AudioSegment.silent(self.min_silence_len) + sound_cut

                if cut_file.duration_seconds * 1000 > self.min_length:  # 检查如果截取到的音频过短，则将其累积到下一条
                    # print("---------")
                    # print(sound_cut.duration_seconds)
                    # print(cut_file.duration_seconds)
                    tmp_file = wav_output + wav_name + "_" + str(i) + ".wav"
                    cut_file.export(tmp_file, format="wav")
                    cut_file_backup = cut_file

                    self.log(f"写入文件为:  {tmp_file} ")
                    self.log(f"写入！为：{cut_file.duration_seconds}秒, 此时备份长度为：{cut_file_backup.duration_seconds}秒")
                    i = i + 1
                    cut_file = AudioSegment.silent(1)
                else:
                    self.log(f"过短！为：{cut_file.duration_seconds}秒,累计")

            if cut_file.duration_seconds >= 0.001:
                self.log(f"需要覆盖，备份的长度为：{cut_file_backup.duration_seconds}秒")
                cut_file = cut_file_backup + AudioSegment.silent(self.min_silence_len) + cut_file
                cut_file.export(wav_output + wav_name + "_" + str(i - 1) + ".wav", format="wav")
                self.log(f"加长后覆盖上一条！为：{cut_file.duration_seconds}秒")

            self.log(f"截取出{i}个音频文件 \n")

            freq += 1
            rate = freq / cnt * 100

            self.signal[float].emit(rate)

    def log(self, data):
        # 通过自定义信号把待显示的字符串传递给槽函数
        self.signal.emit(data)


class MainForm(QtWidgets.QWidget):
    #  通过类成员对象定义信号对象
    _startThread = pyqtSignal([str], [float])

    def __init__(self):
        super(MainForm, self).__init__()
        self.ui = Ui_MainForm()
        self.ui.setupUi(self)
        self.ui.progressBar.setValue(0)

        # 初始化QThread子线程
        self.threadCut = QThread(self)

        # 实例化线程对象
        self.worker = WorkThread()
        # 把自定义线程加入到QThread子线程中
        self.worker.moveToThread(self.threadCut)
        self.worker.signal[str].connect(self.log)
        self.worker.signal[float].connect(self.call_backProcess)

        # 只能通过信号-槽启动线程处理函数
        self._startThread.connect(self.worker.run)

    def choice_button_click(self):
        fileLists, fileType = QtWidgets.QFileDialog.getOpenFileNames(None, "文件选择", project_path, "WaveFiles(*.wav);;All Files(*)")
        self.ui.listWidget.addItems(fileLists)

    def ok_button_click(self):
        min_length = self.ui.spinBox.value()         # 截取出的音频长度不会低于这个值，单位：ms
        min_silence_len = self.ui.spinBox_2.value()  # 当语音间隔长度大于这个值时进行一次截断，单位：ms
        min_to_cut = self.ui.spinBox_3.value()       # 如果原音频短于这个长度则不会被切割，单位：ms
        self.log(f"截取出的音频长度: %d ms" % min_length)
        self.log(f"语音间隔长度: %d ms" % min_silence_len)
        self.log(f"原音频最小长度: %d ms" % min_to_cut)

        paths = []
        for i in range(self.ui.listWidget.count()):
            paths.append(self.ui.listWidget.item(i).text())

        self.worker.reserve(paths, min_length, min_silence_len, min_to_cut)

        # 先启动QThread子线程
        self.log("开始运行......\n")
        self.ui.progressBar.setValue(0)
        self.threadCut.start()
        # 发送信号，启动线程处理函数
        # 不能直接调用，否则会导致线程处理函数和主线程是在同一个线程，同样操作不了主界面
        self._startThread.emit("")

    def log(self, data):
        self.ui.logEdit.append(data)

    def call_backProcess(self, data):
        self.ui.progressBar.setValue(int(data))
        self.log('完成进度 = %.2f %%\n' % data)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainForm()
    main.show()
    sys.exit(app.exec_())
